package com.hexb.smh.aop;

import com.hexb.core.utils.ObjectUtils;
import com.hexb.smh.annotations.AopLoginLog;
import com.hexb.smh.annotations.AopOperationLog;
import com.hexb.smh.entity.Account;
import com.hexb.smh.entity.LoginLog;
import com.hexb.smh.entity.OperationLog;
import com.hexb.smh.entity.enums.ILogCreator;
import com.hexb.smh.entity.enums.LoginType;
import com.hexb.smh.entity.enums.OperationResult;
import com.hexb.smh.entity.enums.Operators;
import com.hexb.smh.service.IOperationLogService;
import com.hexb.smh.service.IProductService;
import com.hexb.smh.utils.AccountHelper;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutorService;

/**
 * @author : hexb
 */
@Component
@Aspect
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class LogAspect {

    @NonNull
    private final ExecutorService executorService;

    @NonNull
    private final IOperationLogService operationLogService;

    @NonNull
    private final OperationLogFactory logFactory;

    @Pointcut("execution (* com.hexb.smh.controller..*.*(..))")
    public void pointCut() {

    }


    @Around("pointCut() && @annotation(annotation)")
    public Object operations(ProceedingJoinPoint point, AopOperationLog annotation) throws Throwable {
        OperationLog ol = OperationLog.builder()
                .accountId(AccountHelper.getAccount().getId())
                .operationName(annotation.name())
                .build();
        Object[] args = point.getArgs();
        Operators operations = annotation.operations();
        try {
            ILogCreator logCreator = logFactory.get(operations);
            ObjectUtils.safeCall(() -> ol.setDetail(logCreator.create(args)));

            Object result = point.proceed();
            ol.setResult(OperationResult.success);
            executorService.execute(() -> operationLogService.save(ol));
            return result;
        } catch (Throwable throwable) {
            ol.setMessage(throwable.getMessage());
            ol.setResult(OperationResult.failed);
            ILogCreator logCreator = logFactory.get(operations);
            ObjectUtils.safeCall(() -> ol.setDetail(logCreator.create(args)));
            executorService.execute(() -> operationLogService.save(ol));
            throw throwable;
        }
    }

    @Around("pointCut() && @annotation(annotation)")
    public Object login(ProceedingJoinPoint point, AopLoginLog annotation) throws Throwable {
        LoginLog ll = LoginLog.builder().type(annotation.type()).build();
        if (annotation.type() == LoginType.logout) {
            ll.setAccountId(AccountHelper.getAccount().getId());
        }
        Object result = point.proceed();
        ObjectUtils.safeCall(() -> {
            Account account = (Account) result;
            ll.setAccountId(account.getId());
            ll.setIp(account.getLastLoginIp());
        });
        executorService.execute(() -> operationLogService.save(ll));
        return result;
    }


}
